Computer programs typically contain two types of code: instructions that carry out the purpose of the program and error handling code that contains instructions for recovering from or responding to unexpected or unusual conditions that occur. Error handling may include addressing anything from a file open failing because the file is not at a requested location to accessing memory that does not belong to the program (e.g., memory that the program did not allocate or has already freed). Software exceptions are one type of error notification paradigm. An exception is the signal raised when a condition is detected that was not expected in the normal execution of a program thread. Many agents can detect incorrect conditions and raise exceptions. For applications running in a managed environment, exceptions can be raised by program code (or library code the program uses), the runtime engine, and unmanaged code that the application invokes. Exceptions allow the developer to write fast and straightforward software code for the normal case, and contain code for handling errors (e.g., exceptions) in a predefined area. Using exceptions, a program typically surrounds a block of instructions with a “try” block and when an abnormal condition occurs, the program leaves the try block and executes one or more conditionally executed blocks.
Exceptions raised on a thread of execution follow the thread through native and managed code and across application domains (e.g., Microsoft .NET AppDomains). If the program does not handle an exception, the exception is typically presented to the operating system and treated as an unhandled exception. A program can handle an exception if it understands the conditions that caused the exception to occur. Exceptions represent a wide range of unexpected conditions during execution of a program. Exceptions may occur at a variety of levels when a program is viewed as a logical stack of layers. For example, the operating system may provide exceptions (e.g., structured exception handling (SEH)), an underlying runtime may provide exceptions (e.g., the C-language runtime or Microsoft .NET Common Language Runtime (CLR)), and the program itself may provide exceptions defined by the language used to create the program (e.g., C-language exceptions, Microsoft .NET exceptions, and so forth). For higher-level languages, the environment may wrap each of these exceptions into one or more types recognized by the language of the program. For example, Microsoft .NET represents any native exception as a specific managed exception that inherits from the Microsoft .NET Exception class.
Software has grown so complex that component reuse is common and many software programs invoke external components or include components (e.g., by static linking) internally that were not written or verified by the program author. For example, a data mining application may invoke a database library for establishing connections to a database and receiving data from the database in response to queries. In addition, programs may include whole platform paradigms that differ from the main paradigm of the program. For example, a native C++ application may invoke managed Microsoft .NET software classes to perform certain actions. Any part of such complex systems may include errors and exceptions that other parts of the system do not anticipate or handle. For example, if a native application invokes managed code, and the managed code hits an exception or other failure, the native application state may be corrupted or the native application may terminate because the native application is unaware of or was not designed to handle the failure. Conversely, a managed application may call into native code that corrupts the application state or experiences some other failure.
Often the most relevant information about an exception for offline analysis is knowing that the exception occurred. Unfortunately, it is difficult for an application, in particular one that includes one or more third-party components or add-ins, to detect every exception and log information about the exception. For example, software code may catch the exception and handle it (correctly or incorrectly) making it impossible for other application code to detect that the exception occurred. If the application is run in a debugger, debuggers commonly provide developers with a notification of a raised exception before the program is allowed to process the exception. However, running applications in a debugger is not practical in regular consumer scenarios.
Various mechanisms for handling exceptions outside of the normal application flow exist today. As one example, applications can provide unhandled exception filters that receive any exception that was not otherwise caught by an application. As another example, Microsoft Windows provides a mechanism called Vectored Exception Handling that allows a programmer to add a filter to a chain of filters executed by the OS when a native exception is raised. Hooking this from managed code can allow similar functionality. However, this is not recommended, and application exception processing may cause changes in the program state, that make it difficult for an unhandled exception filter to log enough relevant details about the cause of the exception. The delivery of an exception to a program's exception handling regions (normally catch or filter blocks) involves the exception system performing a significant amount of work. Program code cannot examine an exception without accepting the exception for processing, which is an action that can modify the exception state and program state before the handler receives the exception.